package com.liujit.gateway.route.service.impl;

import com.liujit.gateway.constants.GateWayConstant;
import com.liujit.gateway.route.domain.GatewayFilterDefinition;
import com.liujit.gateway.route.domain.GatewayPredicateDefinition;
import com.liujit.gateway.route.domain.GatewayRouteDefinition;
import com.liujit.gateway.route.service.DynamicRouteService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.*;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @description: 动态路由实现类
 * @author: liujun
 * @createTime: 2021/6/9 10:22 上午
 **/
@Slf4j
@Service
public class DynamicRouteServiceImpl implements DynamicRouteService {

    @Resource
    private RouteDefinitionWriter routeDefinitionWriter;

    @Resource
    private RouteDefinitionLocator routeDefinitionLocator;

    @Resource
    private RouteLocator routeLocator;

    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public Mono<List<GatewayRouteDefinition>> getList() {
        List<GatewayRouteDefinition> gatewayRouteDefinitions = new ArrayList<>();
        Mono<Map<String, RouteDefinition>> routeDefs = routeDefinitionLocator.getRouteDefinitions().collectMap(RouteDefinition::getId);
        Mono<List<Route>> routes = routeLocator.getRoutes().collectList();
        return Mono.zip(routeDefs, routes).map(tuple -> {
            Map<String, RouteDefinition> defs = tuple.getT1();
            List<Route> routeList = tuple.getT2();
            routeList.forEach(route -> {
                if (defs.containsKey(route.getId())) {
                    RouteDefinition routeDefinition = defs.get(route.getId());
                    routeDefinition.setId(route.getId());
                    routeDefinition.setOrder(route.getOrder());
                    // 路由转换
                    GatewayRouteDefinition gatewayRouteDefinition = convertGateway(routeDefinition);
                    gatewayRouteDefinitions.add(gatewayRouteDefinition);
                }
            });
            return gatewayRouteDefinitions;
        });
    }

    @Override
    public Boolean add(GatewayRouteDefinition gatewayRouteDefinition) {
        RouteDefinition routeDefinition = convert(gatewayRouteDefinition);
        try {
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
            return Boolean.TRUE;
        } catch (Exception e) {
            log.error("路由添加异常，路由id:{}", gatewayRouteDefinition.getId());
            return Boolean.FALSE;
        }
    }

    @Override
    public Boolean del(String id) {
        try {
            routeDefinitionWriter.delete(Mono.just(id));
            return Boolean.TRUE;
        } catch (Exception e) {
            log.error("路由删除异常，路由id:{}", id);
            return Boolean.FALSE;
        }
    }

    @Override
    public Boolean upd(GatewayRouteDefinition gatewayRouteDefinition) {
        RouteDefinition routeDefinition = convert(gatewayRouteDefinition);
        try {
            // 删除路由
            routeDefinitionWriter.delete(Mono.just(routeDefinition.getId()));
            // 添加路由
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
            return Boolean.TRUE;
        } catch (Exception e) {
            log.error("路由更新异常，路由id:{}", gatewayRouteDefinition.getId());
            return Boolean.FALSE;
        }
    }

    @Override
    public Mono<GatewayRouteDefinition> get(String id) {
        return routeDefinitionLocator.getRouteDefinitions().filter(
                item -> item.getId().equals(id)).singleOrEmpty().map(tuple -> convertGateway(tuple));
    }

    @Override
    public Boolean refresh() {
        applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
        return Boolean.TRUE;
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }


    /**
     * 把传递进来的参数转换成路由对象
     *
     * @param gatewayRouteDefinition
     * @return:
     * @author: liujun
     * @date: 2021/6/9 10:10 上午
     */
    private RouteDefinition convert(GatewayRouteDefinition gatewayRouteDefinition) {
        RouteDefinition routeDefinition = new RouteDefinition();
        routeDefinition.setId(gatewayRouteDefinition.getId());
        routeDefinition.setOrder(gatewayRouteDefinition.getOrder());
        // 设置断言
        List<PredicateDefinition> predicateDefinitions = new ArrayList<>();
        List<GatewayPredicateDefinition> gatewayPredicateDefinitions = gatewayRouteDefinition.getPredicates();
        if (gatewayPredicateDefinitions != null && gatewayPredicateDefinitions.size() > 0) {
            gatewayPredicateDefinitions.forEach(item -> {
                predicateDefinitions.add(new PredicateDefinition() {{
                    setName(item.getName());
                    setArgs(item.getArgs());
                }});
            });
        }
        routeDefinition.setPredicates(predicateDefinitions);
        // 设置过滤器
        List<FilterDefinition> filterDefinitions = new ArrayList();
        List<GatewayFilterDefinition> gatewayFilterDefinitions = gatewayRouteDefinition.getFilters();
        if (gatewayFilterDefinitions != null && gatewayFilterDefinitions.size() > 0) {
            gatewayFilterDefinitions.forEach(item -> {
                filterDefinitions.add(new FilterDefinition() {{
                    setName(item.getName());
                    setArgs(item.getArgs());
                }});
            });
        }
        routeDefinition.setFilters(filterDefinitions);

        if (gatewayRouteDefinition.getUri().startsWith(GateWayConstant.HTTP)) {
            routeDefinition.setUri(UriComponentsBuilder.fromHttpUrl(gatewayRouteDefinition.getUri()).build().toUri());
        } else {
            // uri为 lb://xxx 时使用下面的方法
            routeDefinition.setUri(URI.create(gatewayRouteDefinition.getUri()));
        }
        return routeDefinition;
    }

    /**
     * 把路由对象转换成返回对象
     *
     * @param routeDefinition
     * @return:
     * @author: liujun
     * @date: 2021/6/9 10:10 上午
     */
    private GatewayRouteDefinition convertGateway(RouteDefinition routeDefinition) {
        GatewayRouteDefinition gatewayRouteDefinition = new GatewayRouteDefinition();
        if (routeDefinition != null) {
            gatewayRouteDefinition.setId(routeDefinition.getId());
            gatewayRouteDefinition.setOrder(routeDefinition.getOrder());
            // 设置断言
            List<GatewayPredicateDefinition> gatewayPredicateDefinitions = new ArrayList<>();
            List<PredicateDefinition> predicateDefinitions = routeDefinition.getPredicates();
            if (predicateDefinitions != null && predicateDefinitions.size() > 0) {
                predicateDefinitions.forEach(item -> {
                    gatewayPredicateDefinitions.add(new GatewayPredicateDefinition() {{
                        setName(item.getName());
                        setArgs(item.getArgs());
                    }});
                });
            }
            gatewayRouteDefinition.setPredicates(gatewayPredicateDefinitions);
            // 设置过滤器
            List<GatewayFilterDefinition> gatewayFilterDefinitions = new ArrayList();
            List<FilterDefinition> filterDefinitions = routeDefinition.getFilters();
            if (filterDefinitions != null && filterDefinitions.size() > 0) {
                filterDefinitions.forEach(item -> {
                    gatewayFilterDefinitions.add(new GatewayFilterDefinition() {{
                        setName(item.getName());
                        setArgs(item.getArgs());
                    }});
                });
            }
            routeDefinition.setFilters(filterDefinitions);
            routeDefinition.setUri(routeDefinition.getUri());
        }
        return gatewayRouteDefinition;
    }
}
